package com.ecarx.module_vehicle.signal.controller;


import android.car.Car;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.CarPropertyManager;
import android.text.TextUtils;
import android.util.Log;

import com.ecarx.library_base.BaseApplication;
import com.ecarx.module_vehicle.signal.Model.AreaId;
import com.ecarx.module_vehicle.signal.Model.InPropertyValue;
import com.ecarx.module_vehicle.signal.Model.Rate;
import com.ecarx.module_vehicle.signal.Model.SignalItem;
import com.ecarx.module_vehicle.signal.Model.SignalModel;
import com.ecarx.module_vehicle.signal.Model.WrappedPropertyId;
import com.ecarx.xui.adaptapi.car.IWrapper;

import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class VsController {

    private static final String TAG = "VsController";

    private IWrapper mWrapper;
    private CarPropertyManager mCarPropertyMgr;

    private EventCallback mEventCallback;
    private final Map<String, List<SignalItem>> mSignalModel = new LinkedHashMap<>();
    private Map<String, StringBuilder> mLogRecordMap;

    private final SimpleDateFormat mSdf = new SimpleDateFormat("HH:mm:ss:SSS", Locale.getDefault());


    public VsController() {

    }

    public void init(EventCallback eventCallback) {
        this.mEventCallback = eventCallback;
        Car mCar = Car.createCar(BaseApplication.getInstance());
        mCarPropertyMgr = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE);
        mWrapper = com.ecarx.xui.adaptapi.car.Car.createWrapper(BaseApplication.getInstance());
        Map<String, List<SignalItem>> originalSignalModel = SignalModel.initSignalModelFormXML();
        mLogRecordMap = Collections.synchronizedMap(new HashMap<>());
        //部分adapt的参数使用时要换成AOSP CarPorp中的实际值，这里将要换的接口参数在初始化时全换了
        originalSignalModel.entrySet().forEach(new Consumer<Map.Entry<String, List<SignalItem>>>() {
            @Override
            public void accept(Map.Entry<String, List<SignalItem>> stringSignalItemEntry) {
                String moduleName = stringSignalItemEntry.getKey();

                List<SignalItem> signalItemList = stringSignalItemEntry.getValue();
                List<SignalItem> collect = signalItemList.stream().filter(new Predicate<SignalItem>() {
                    @Override
                    public boolean test(SignalItem signalItem) {
                        WrappedPropertyId wrappedPropertyId = signalItem.getWrappedPropertyId();

                        if (wrappedPropertyId.hasAospId()) {
                            return true;
                        }
                        //在这里获取并保存IWrapper.IPropertyId
                        IWrapper.IPropertyId propertyId = mWrapper.getWrappedPropertyId(IWrapper.WrappedIdType.ID_TYPE_FUNCTION, Integer.parseInt(wrappedPropertyId.getAdaptId()));
                        if (propertyId == null) {
                            Log.d(TAG, "init: " + wrappedPropertyId.getShortName() + " " + wrappedPropertyId.getAdaptId() + " getWrappedPropertyId =null");
                            return false;
                        }
                        wrappedPropertyId.setAospId(String.valueOf(propertyId.getPropertyId()));

                        List<InPropertyValue> inParams = signalItem.getInPropertyValues();
                        inParams.forEach(new Consumer<InPropertyValue>() {
                            @Override
                            public void accept(InPropertyValue inPropertyValue) {
                                //adapt参数需要在这里获取aosp id
                                if (!TextUtils.isEmpty(inPropertyValue.getAdaptId())) {
                                    inPropertyValue.setAospId(String.valueOf(propertyId.getPropertyValue(Integer.parseInt(inPropertyValue.getAdaptId()))));
                                }
                            }
                        });
                        return true;
                    }
                }).collect(Collectors.toList());
                if (!collect.isEmpty()) {
                    mSignalModel.put(moduleName, collect);
                    mLogRecordMap.put(moduleName, new StringBuilder());
                }

            }
        });
    }

    public Map<String, List<SignalItem>> getSignalModel() {
        return mSignalModel;
    }

    public Map<String, StringBuilder> getLogRecordMap() {
        return mLogRecordMap;
    }

    public void executeGet(String moduleName, WrappedPropertyId wrappedPropertyId, AreaId areaId) {
        int propId = wrappedPropertyId.getAospInt();
        try {
            Object result = "";
            switch (wrappedPropertyId.getType()) {
                case "boolean":
                    result = mCarPropertyMgr.getBooleanProperty(propId, areaId.getAospInt());
                    break;
                case "int":
                    result = mCarPropertyMgr.getIntProperty(propId, areaId.getAospInt());
                    break;
                case "float":
                    result = mCarPropertyMgr.getFloatProperty(propId, areaId.getAospInt());
                    break;
                default:
                    break;
            }
            doLogRecord(moduleName, "get:" + wrappedPropertyId.getShortName() + " " + areaId.getShortName() + " "
                    + getPropShortNameBy(moduleName, wrappedPropertyId, result));
        } catch (Exception e) {
            doLogRecord(moduleName, "get: failed " + wrappedPropertyId.getShortName() + " " + e.getMessage());
            Log.e(TAG, "executeGet: ", e);
        }
    }

    public void executeSet(String moduleName, WrappedPropertyId wrappedPropertyId, AreaId areaId, InPropertyValue inPropertyValue) {

        try {
            int propId = wrappedPropertyId.getAospInt();
            if (!mCarPropertyMgr.isPropertyAvailable(propId, areaId.getAospInt())) {
                doLogRecord(moduleName, "set: failed " + wrappedPropertyId.getShortName() + " isNotAvailable");
                return;
            }
            String value = inPropertyValue.getAospId();
            switch (wrappedPropertyId.getType()) {
                case "boolean":
                    mCarPropertyMgr.setBooleanProperty(propId, areaId.getAospInt(), Boolean.parseBoolean(value));
                    break;
                case "int":
                    mCarPropertyMgr.setIntProperty(propId, areaId.getAospInt(), Integer.parseInt(value));
                    break;
                case "float":
                    mCarPropertyMgr.setFloatProperty(propId, areaId.getAospInt(), Float.parseFloat(value));
                    break;
                default:
                    break;
            }
            doLogRecord(moduleName, "set: " + wrappedPropertyId.getShortName() + " " + areaId.getShortName() + " " +
                    inPropertyValue.getShortName());
        } catch (Exception e) {
            doLogRecord(moduleName, "set: failed " + wrappedPropertyId.getShortName() + " " + e.getMessage());
            Log.e(TAG, "executeSet: ", e);
        }
    }

    public void registerCallback(String moduleName, WrappedPropertyId wrappedPropertyId, Rate rate) {
        boolean canGet = wrappedPropertyId.isCanGet();
        if (!canGet) {
            doLogRecord(moduleName, "registerCallback Unsupported: " + wrappedPropertyId.getShortName());
            return;
        }
        int propId = wrappedPropertyId.getAospInt();
        mCarPropertyMgr.registerCallback(mPropertyEventCallback, propId, rate.getRate());
        Log.d(TAG, "registerCallback: " + propId);
        doLogRecord(moduleName, "registerCallback: " + wrappedPropertyId.getShortName() + " " + rate.getName());
    }

    public void unRegisterCallback(String moduleName, WrappedPropertyId wrappedPropertyId) {
        boolean canGet = wrappedPropertyId.isCanGet();
        if (!canGet) {
            doLogRecord(moduleName, "unRegisterCallback Unsupported: " + wrappedPropertyId.getShortName());
            return;
        }
        int propId = wrappedPropertyId.getAospInt();
        mCarPropertyMgr.unregisterCallback(mPropertyEventCallback, propId);
        Log.d(TAG, "unRegisterCallback: " + propId);
        doLogRecord(moduleName, "unRegisterCallback: " + wrappedPropertyId.getShortName());
    }

    private final CarPropertyManager.CarPropertyEventCallback mPropertyEventCallback = new CarPropertyManager.CarPropertyEventCallback() {
        @Override
        public void onChangeEvent(CarPropertyValue carPropertyValue) {
            int propertyId = carPropertyValue.getPropertyId();
            int areaId = carPropertyValue.getAreaId();
            Object value = carPropertyValue.getValue();
            Log.d(TAG, "onChangeEvent: " + propertyId + " " + areaId + " " + value + " " + carPropertyValue.getStatus());
            mSignalModel.entrySet().forEach(new Consumer<Map.Entry<String, List<SignalItem>>>() {
                @Override
                public void accept(Map.Entry<String, List<SignalItem>> stringListEntry) {
                    List<SignalItem> signalItemList = stringListEntry.getValue();
                    for (SignalItem signalItem : signalItemList) {

                        WrappedPropertyId wrappedPropertyId = signalItem.getWrappedPropertyId();
                        if (propertyId == wrappedPropertyId.getAospInt()) {
                            String areaIdName = String.valueOf(areaId);
                            for (AreaId areaIdv : signalItem.getAreaIdList()) {
                                if (areaIdv.getAospInt() == areaId) {
                                    areaIdName = areaIdv.getShortName();
                                    break;
                                }
                            }

                            String log = "callback: " + wrappedPropertyId.getShortName() + " " + areaIdName + " "
                                    + getPropShortNameBy(stringListEntry.getKey(), wrappedPropertyId, value);
                            doLogRecord(stringListEntry.getKey(), log);
                            return;
                        }
                    }
                }
            });
        }

        @Override
        public void onErrorEvent(int propId, int zone) {
            doLogRecord(getModuleNameByPropId(propId), "callback error: propId " + propId + " zone " + zone);
        }
    };

    private String getPropShortNameBy(String moduleName, WrappedPropertyId id, Object targetId) {
        String result = String.valueOf(targetId);
        List<SignalItem> signalItems = mSignalModel.get(moduleName);
        if (signalItems != null) {
            for (SignalItem signalItem : signalItems) {
                WrappedPropertyId wrappedPropertyId = signalItem.getWrappedPropertyId();
                if (wrappedPropertyId.getName().equals(id.getName())) {
                    for (InPropertyValue in : signalItem.getInPropertyValues()) {
                        if (result.equals(in.getAospId())) {
                            result = in.getShortName();
                            return result;
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * 通过id反查模块名
     */
    private String getModuleNameByPropId(int propertyId) {
        final String[] moduleName = {""};
        mSignalModel.entrySet().forEach(new Consumer<Map.Entry<String, List<SignalItem>>>() {
            @Override
            public void accept(Map.Entry<String, List<SignalItem>> stringListEntry) {

                List<SignalItem> signalItemList = stringListEntry.getValue();
                for (SignalItem signalItem : signalItemList) {
                    WrappedPropertyId wrappedPropertyId = signalItem.getWrappedPropertyId();
                    if (propertyId == wrappedPropertyId.getAospInt()) {
                        moduleName[0] = signalItem.getModuleName();
                        return;
                    }
                }
            }
        });
        return moduleName[0];
    }

    /**
     * 回调输出log
     *
     * @param module 模块名
     * @param log    日志
     */
    private void doLogRecord(String module, String log) {
        StringBuilder stringBuilder = mLogRecordMap.get(module);
        if (stringBuilder != null) {
            stringBuilder.append(mSdf.format(new Date())).append(": ").append(log).append("\n");
            //当存当log字数大于2000，每次新加一行都删除第一行
            if (stringBuilder.length() > 6000) {
                int index = stringBuilder.indexOf("\n");
                if (index > 0) {
                    stringBuilder.delete(0, index + 1);
                }
            }
            mEventCallback.onLogChanged(module, stringBuilder.toString());
        }

        Log.d(TAG, "doLogRecord: " + module + " " + log);
    }

    public void clearLogByModuleName(String moduleName) {
        try {
            StringBuilder stringBuilder = mLogRecordMap.get(moduleName);
            stringBuilder.delete(0, stringBuilder.length() + 1);
            mEventCallback.onLogChanged(moduleName, "");
        } catch (Exception e) {
            Log.e(TAG, "clearLogByModuleName: ", e);
        }
    }


    public interface EventCallback {
        /**
         * log结果回调
         *
         * @param moduleName 模块名
         * @param log        日志
         */
        void onLogChanged(String moduleName, String log);
    }

}
